package com.enterpriseDigitalPlatform.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;

import java.time.Duration;
import java.util.Map;

@Slf4j
public class RestUtil {


    private RestUtil() {

    }

    private static final Integer DEFAULT_CONNECT_TIMEOUT = 3000;

    private static final Integer DEFAULT_REQUEST_TIMEOUT = 10000;

    // todo 无参数get请求
    public static <T> T requestGet(String url, Class<T> tClass) throws JsonProcessingException {
        // 直接处理
        Mono<T> resp = createWebClientWithConnectAndReadTimeOuts()
                // 请求方法
                .get()
                // 设置请求头
                // .headers(t -> t.setAll(headers))
                // 请求路径
                .uri(url)
                // 获取响应结果
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.empty())
                // 将结果转换为指定类型
                .bodyToMono(tClass)
                .timeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT));
        return resp.block();
    }

    // todo 数字占位符方式传参
    // 参数名占位符
    public static <T> T requestGet(String url, Class<T> tClass, Object... uriVariables) throws JsonProcessingException {
        // 参数 示例  以逗号分隔
        // 2, "zhaoxb"
        // url 示例
        // "http://127.0.0.1:31000/getUser2?id={1}&name={2}"
        // 参数名传参
        // "http://localhost:8080/user/{id}/{name}";
        // String id = "123";
        // String name = "Boss";
        Mono<T> resp = createWebClientWithConnectAndReadTimeOuts()
                // 请求方法
                .get()
                // 设置请求头
                // .headers(t -> t.setAll(headers))
                // 请求路径
                .uri(url, uriVariables)
                // 获取响应结果
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.empty())
                // 将结果转换为指定类型
                .bodyToMono(tClass)
                .timeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT));
        return resp.block();
    }

    // todo map占位符方式传参
    public static <T> T requestGet(String url, Map<String, Object> map, Class<T> tClass) throws JsonProcessingException {
        // map 示例
        // map Map<String, Object> map = new HashMap<>();
        // map.put("id", 3);
        // map.put("name", "zhaoxb");
        // url 格式
        // "http://127.0.0.1:31000/getUser2?id={id}&name={name}"
        Mono<T> resp = createWebClientWithConnectAndReadTimeOuts()
                // 请求方法
                .get()
                // 设置请求头
                // .headers(t -> t.setAll(headers))
                // 请求路径
                .uri(url, map)
                // 获取响应结果
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.empty())
                // 将结果转换为指定类型
                .bodyToMono(tClass)
                .timeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT));
        return resp.block();
    }

    // todo post 请求 application/x-www-form-urlencoded表单传参
    public static <T> T requestPostForm(String url, MultiValueMap<String, String> map, Class<T> tClass) throws JsonProcessingException {
        // 请求参数 示例
        // MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        // map.add("msgKey", "1234");
        // map.add("msgValue", "TestTest");
        Mono<T> resp = createWebClientWithConnectAndReadTimeOuts()
                .post()
                .uri(url)
                // 设置请求头为form形式: application/x-www-form-urlencoded
                // .header("application", "x-www-form-urlencoded")
                .accept(MediaType.valueOf("application/json;charset=utf-8;"))
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(map))
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.empty())
                .bodyToMono(tClass).timeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT));
        return resp.block();
    }

    // todo post 请求 application/x-www-form-urlencoded表单传参
    public static <T> T requestPostForm(String url, MultiValueMap<String, String> map, Map<String, String> headers, Class<T> tClass) throws JsonProcessingException {
        // 请求参数 示例
        // MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        // map.add("msgKey", "1234");
        // map.add("msgValue", "TestTest");
        Mono<T> resp = createWebClientWithConnectAndReadTimeOuts()
                .post()
                .uri(url)
                // 设置请求头为form形式: application/x-www-form-urlencoded
                .headers(t -> t.setAll(headers))
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(map))
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.empty())
                .bodyToMono(tClass).timeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT));
        return resp.block();
    }

    // todo post application/json JSON传参请求
    public static <T> T requestPostJson(String url, Map<String, Object> map, Class<T> tClass) throws JsonProcessingException {
        // map 示例
        // map Map<String, Object> map = new HashMap<>();
        // map.put("id", 3);
        // map.put("name", "zhaoxb");
        Mono<T> resp = createWebClientWithConnectAndReadTimeOuts()
                .post()
                .uri(url)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(Mono.just(map), Object.class)
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.empty())
                .bodyToMono(tClass).timeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT));
        return resp.block();
    }

    // todo post application/json JSON传参请求
    public static <T> T requestPostJson(String url, Map<String, Object> map, Map<String, String> headers, Class<T> tClass) throws JsonProcessingException {
        // map 示例
        // map Map<String, Object> map = new HashMap<>();
        // map.put("id", 3);
        // map.put("name", "zhaoxb");
        Mono<T> resp = createWebClientWithConnectAndReadTimeOuts()
                .post()
                .uri(url)
                .headers(t -> t.setAll(headers))
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(Mono.just(map), Object.class)
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.empty())
                .bodyToMono(tClass).timeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT));
        return resp.block();
    }

    // todo post application/xml JSON传参请求
    public static <T> T requestPostXml(String url, String map, Class<T> tClass) throws JsonProcessingException {
        // map 示例
        // map Map<String, Object> map = new HashMap<>();
        // map.put("id", 3);
        // map.put("name", "zhaoxb");
        Mono<T> resp = createWebClientWithConnectAndReadTimeOuts()
                .post()
                .uri(url)
                .contentType(MediaType.APPLICATION_XML)
                .body(Mono.just(map), String.class)
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.empty())
                .bodyToMono(tClass).timeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT));
        return resp.block();
    }

    // todo post application/xml JSON传参请求
    public static <T> T requestPostXml(String url, String map, Map<String, String> headers, Class<T> tClass) throws JsonProcessingException {
        // map 示例
        // map Map<String, Object> map = new HashMap<>();
        // map.put("id", 3);
        // map.put("name", "zhaoxb");
        Mono<T> resp = createWebClientWithConnectAndReadTimeOuts()
                .post()
                .uri(url)
                .headers(t -> t.setAll(headers))
                .contentType(MediaType.APPLICATION_XML)
                .body(Mono.just(map), Object.class)
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.empty())
                .bodyToMono(tClass).timeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT));
        return resp.block();
    }


    // 创建webclient客户端
    private static WebClient createWebClientWithConnectAndReadTimeOuts() {
        // create reactor netty HTTP client
        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, DEFAULT_CONNECT_TIMEOUT)
                .responseTimeout(Duration.ofMillis(DEFAULT_REQUEST_TIMEOUT))
                .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5000)));
        // create a client http connector using above http client
        ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
        // use this configured http connector to build the web client
        return WebClient.builder().clientConnector(connector).build();
    }
}
